/* Copyright 2014 Jan Wolter */

#include "stack.h"

/* Basic card facts */
int ndeck= 1;
int nsuit= 4;
int npip= 13;
int ncolor= 2;
int suitcolor[]= {0,1,0,1};
char *suitletter= "SHCD";
char *rankletter= " A23456789TJQK";

/* This array contains the actual storage for the Card structures. They are
 * always in order. Everything else points to these.
 */

Card *deck= NULL;
int ncard;

/* Construct the global deck object
 */

void makeDeck()
{
    if (deck != NULL) free(deck);

    ncard= ndeck*nsuit*npip;
    deck= (Card *)malloc(ncard*sizeof(Card));
    int n= 0;
    for (int d= 0; d < ndeck; d++)
	for (int s= 0; s < nsuit; s++)
	    for (int i= 1; i <= npip; i++)
	    {
		deck[n].n= i;
		deck[n].s= s;
		deck[n].faceup= 1;
		n++;
	    }
}

/* If we are re-using the previous deck instead of making a new one, clean 
 * it up.
 */
void cleanDeck()
{
    for (int i= 0; i < ncard; i++)
	deck[i].faceup= 1;
}

/* Build a shuffled deck.  Returns a newly allocated temp stack which the
 * caller must free with freeStack().
 */
Stack *shuffle()
{
    makeDeck();

    Stack *shuf= allocStack(ncard);

    for (int n= 0, m= 0; n < ncard; n++)
    {
	if (n > 0)
	{
	    m= randint(n+1);
	    shuf->c[n]= shuf->c[m];
	}
	shuf->c[m]= deck+n;
    }
    shuf->n= ncard;

    return shuf;
}

/* This is to be called on a shuffled deck. Given a rank and suit (if the
 * suit is -1, any card of that rank will do), this (1) removes the card from
 * the shuffled deck, and (2) returns a pointer to the card in the global
 * deck array. Returns NULL if no such card is found.
 */
Card *extractCard(Stack *shuf, int n, int s)
{
    for (int i= 0; i < shuf->n; i++)
	if (shuf->c[i]->n == n && (s == -1 || shuf->c[i]->s == s))
	{
	    Card *c= shuf->c[i];
	    if (i < shuf->n - 1)
		shuf->c[i]= shuf->c[shuf->n-1];
	    shuf->n--;
	    return c;
	}
    return NULL;
}

/* Build a sorted deck in a newly allocated temp stack.
 */

Stack *sorted()
{
    makeDeck();

    Stack *s= allocStack(ncard);

    for (int n= 0; n < ncard; n++)
	s->c[n]= deck+n;
    s->n= ncard;
    return s;
}

/* Build a reverse sorted deck in a newly allocated temp stack.
 */

Stack *revsorted()
{
    makeDeck();

    Stack *s= allocStack(ncard);

    for (int n= 0; n < ncard; n++)
	s->c[n]= deck+ncard-1-n;
    s->n= ncard;
    return s;
}


/* Return a pointer to the top of the stack, without removing it */
Card *topStack(Stack *stack)
{
    if (stack->n == 0) return NULL;
    return stack->c[stack->n-1];
}

/* Remove the top card from the stack and return it */
Card *popStack(Stack *stack)
{
    if (stack->n == 0) return NULL;
    return stack->c[--stack->n];
}

/* Append a card into a stack, allocating more memory if necessary */
void pushStack(Stack *stack, Card *card)
{
    expandStack(stack, stack->n+1, 7);
    stack->c[stack->n++]= card;
}

/* Append all the cards from Stack "from" to stack "to" reversing their order
 * if "reverse" is true.
 */
void appendStack(Stack *to, Stack *from, int reverse)
{
    int n= to->n + from->n;
    expandStack(to,n,0);

    if (reverse)
	for (int i= from->n-1; i >= 0; i--)
	    to->c[to->n++]= from->c[i];
    else
	for (int i= 0; i < from->n; i++)
	    to->c[to->n++]= from->c[i];
}

/* Delete a sequence of cards from a stack. They can be at the end or
 * somewhere in the middle.
 */
void removeStack(Stack *s, int index1, int len)
{
    int after= s->n - index1 - len;
    if (after > 0)
	memmove(s->c + index1, s->c + index1 + len, after * sizeof(Card *));
    s->n-= len;
}

/* Open a gap of the given length starting at the given offset in the given
 * stack, and insert the first len cards from the given array of cards into it.
 * If reverese is true insert the stack in reverse order.
 */

void insertStack(Stack *s, int index1, int len, Card **c, int reverse)
{
    expandStack(s, s->n + len, 0);
    int after= s->n - index1;
    if (after > 0)
	memmove(s->c + index1 + len, s->c + index1, after*sizeof(Card *));
    if (reverse)
    {
	int last= index1+len-1;
	for (int i= 0; i < len; i++)
	    s->c[last-i]= c[i];
    }
    else
	memmove(s->c + index1, c, len * sizeof(Card *));

    s->n+= len;
}

/* Makes sure the stack is big enough for n Cards. If it is too small,
 * enlarge it to size n+pad.
 */
void expandStack(Stack *st, int n, int pad)
{
    if (n > st->size)
    {
	st->size= n + pad;
	st->c= (Card **)realloc(st->c, st->size * sizeof(Card *));
    }
}

/* Allocate a temporary stack with enough space for n cards */
Stack *allocStack(int n)
{
    Stack *st= (Stack *)malloc(sizeof(Stack));
    st->c= (Card **)malloc(n*sizeof(Card *));
    st->size= n;
    st->n= 0;
    return st;
}

/* Deallocate a stack - just the ones created by allocStack() */
void freeStack(Stack *st)
{
    free(st->c);
    free(st);
}

/* We maintain globals 'stacks' 'nstacks' which are arrays of all the stacks
 * in the game. The index into this array is the stack ID.
 */

Stack *stacks;
int nstacks= 0; /* Used stacks */
int astacks= 0;	/* Allocated stacks */
int srclist[100];	/* list of stacks cards can be moved from */
int dstlist[100];	/* list of stacks cards can be moved to */
int nsrc= 0, ndst= 0;

/* Create a new stack. Return it's index in the global stack array */
int newStack(int type, int tid, int initsize,
    int (*mayInsert)(Stack *s, Card *card, int len, Stack *src),
    int (*firstMove)(Stack *s, int *index1, int *len),
    int (*nextMove) (Stack *s, int *index1, int *len),
    void (*postMove)(Stack *stack) )
{
    /* Increase the size of the stacks list if necessary */
    if (astacks == 0)
    {
	astacks= 10;
	stacks= (Stack *)malloc(astacks*sizeof(Stack));
    }
    else if (nstacks == astacks)
    {
	astacks+= 10;
	stacks= (Stack *)realloc(stacks,astacks*sizeof(Stack));
    }
    stacks[nstacks].n= 0;
    stacks[nstacks].size= initsize;
    stacks[nstacks].c= (Card **)malloc(initsize*sizeof(Card *));
    stacks[nstacks].type= type;
    stacks[nstacks].tid= tid;
    stacks[nstacks].mayInsert= mayInsert;
    if (mayInsert != NULL) dstlist[ndst++]= nstacks;
    stacks[nstacks].firstMove= firstMove;
    if (firstMove != NULL) srclist[nsrc++]= nstacks;
    stacks[nstacks].nextMove= nextMove;
    stacks[nstacks].postMove= postMove;
    
    return nstacks++;
}

/* Make a duplicate of the current state of the global 'stacks' structure. */
Stack *dupStacks(Stack *orig, int norig)
{
    Stack *dup= (Stack *)malloc(norig*sizeof(Stack));
    copyStacks(dup, orig, norig, 1);
    return dup;
}

void copyStacks(Stack *copy, Stack *orig, int norig, int isnew)
{
    for (int i= 0; i < norig; i++)
    {
	copy[i].n= orig[i].n;
	if (isnew || copy[i].size < copy[i].n)
	    copy[i].size= copy[i].n;
	copy[i].type= orig[i].type;
	copy[i].tid= orig[i].tid;
	copy[i].c= isnew ? (Card **)malloc(copy[i].size*sizeof(Card *)) :
	       (Card **)realloc(copy[i].c, copy[i].size*sizeof(Card *));
	memmove(copy[i].c, orig[i].c, copy[i].n*sizeof(Card *));
    }
}

void cleanDup(Stack *stks)
{
    for (int i= 0; i < nstacks; i++)
	free(stks[i].c);
    free(stks);
}

/* End of game cleanup of all allocated stacks */
void cleanStacks()
{
    cleanDup(stacks);
    nstacks= 0;
    astacks= 0;
    nsrc= 0;
    ndst= 0;
}

void printCard(FILE *f, Card *c)
{
    fprintf(f,"%c%c", rankletter[c->n], suitletter[c->s]);
}

/* Return a two-letter name for a card in static memory */
char *cardname(Card *c)
{
    static char buf[3];
    buf[0]= rankletter[c->n];
    buf[1]= suitletter[c->s];
    buf[2]= '\0';
    return buf;
}

void printStack(FILE *f, char *label, Stack *st)
{
    int indent;

    if (label == NULL || *label == '\0')
	indent= 0;
    else
    {
	indent= strlen(label)+2;
	fprintf(f,"%s: ",label);
    }

    int col= indent;

    for (int i= 0; i < st->n; i++)
    {
	if (col > 75)
	{
	    fputc('\n',f);
	    for (int j= 0; j < indent; j++)
		fputc(' ',f);
	    col= indent;
	}
	if (col > 0) fputc(' ',f);
	int highlight= !(st->c[i]->faceup);
	if (highlight) fprintf(f,"\033[7m");
	printCard(f,st->c[i]);
	if (highlight) fprintf(f,"\033[0m");
	col+= 3;
    }
    fputc('\n',f);
}

/* Inverse of printStack - makeDeck() should already have been called */
Stack *readStack(char *str)
{
    Stack *st= allocStack(ncard);

    for (;*str != '\0';str++)
    {
	if (*str == ' ' || *str == '\n') continue;

	char *p= strchr(rankletter,*str);
	if (!p) continue;
	int rank= p-rankletter;
	str++;
	p= strchr(suitletter,*str);
	if (!p) continue;
	int suit= p-suitletter;
	int index= suit*npip + rank-1;
	Card *c= deck + index;
	int inst= 0;
	for (int i= 0; i < st->n; i++)
	    if (st->c[i] == c)
	    {
		if (++inst >= ndeck) return NULL;
		index+= nsuit*npip;
		c= deck + index;
	    }
	st->c[st->n++]= c;
    }

    return st;
}

int suitmatch(int rule, int s1, int s2)
{
    switch (rule)
    {
	case S_NONE:
	    return 0;
	case S_SAMESUIT:
	    return s1==s2;
	case S_DIFFSUIT:
	    return s1!=s2;
	case S_SAMECOLOR:
	    return suitcolor[s1]==suitcolor[s2];
	case S_DIFFCOLOR:
	    return suitcolor[s1]!=suitcolor[s2];
	case S_ANYTHING:
	    return 1;
    }
}


/* Do the ranks differ by the given step? If 'base' is zero, there is no
 * base card and wrapping is allowed.  Otherwise it is the value of the
 * minimum card, and you may not wrap from minimum to maximum.
 */

int rankmatch(int step, int base, int r1, int r2)
{
    /* No rank rule - any card can be on any card */
    if (step >= npip) return 1;

    if (base == 0)
	/* Wrapping allowed */
	return ((r1+step+npip-1)%npip+1 == r2);

    if (r1 < base) r1+= npip;
    if (r2 < base) r2+= npip;
    return (r1+step == r2);
}

/* Is the given sequence of cards coherent under the given suit and rank rules? */
int checkseq(int suitrule, int rankrule, int base, Card **c, int len)
{
    for (int i= 0; i < len - 1; i++)
	if (!suitmatch(suitrule, c[i]->s, c[i+1]->s) ||
	    !rankmatch(rankrule, base, c[i]->n, c[i+1]->n))
	    return 0;
    return 1;
}

Move *newMove(int type, int srcid, int srci, int len, int dstid, int score,
    int flip)
{
    Move *m= (Move *)malloc(sizeof(Move));
    m->type= type;
    m->srcid= srcid;
    m->srci= srci;
    m->len= len;
    m->dstid= dstid;
    m->flip= flip;
    m->score= score;
    m->reverse= 0;
    m->prev= m->alt= NULL;
    return m;
}

